/*
 * Tranquil Java Integrated Development Environment
 *
 * The GNU General Public License Version 3
 *
 * Copyright (C) 2021 Autumn Lamonte
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * @author Autumn Lamonte [AutumnWalksTheLake@gmail.com] ⚧ Trans Liberation Now
 * @version 1
 */
package tjide.project;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import tjide.debugger.Breakpoint;

/**
 * FileTarget compiles one source file into a build file.
 */
public abstract class FileTarget extends Target {

    // ------------------------------------------------------------------------
    // Variables --------------------------------------------------------------
    // ------------------------------------------------------------------------

    /**
     * Breakpoints for this target.
     */
    protected List<Breakpoint> breakpoints = new ArrayList<Breakpoint>();

    // ------------------------------------------------------------------------
    // Constructors -----------------------------------------------------------
    // ------------------------------------------------------------------------

    /**
     * Public constructor.
     *
     * @param name the source filename, relative to the project's source
     * directory name.  This will also be the name seen in the project
     * window.
     * @param buildFilename the name of the generated file of the target,
     * relative to the project's build output directory
     */
    public FileTarget(final String name, final String buildFilename) {

        super(name, buildFilename);
    }

    // ------------------------------------------------------------------------
    // FileTarget -------------------------------------------------------------
    // ------------------------------------------------------------------------

    /**
     * Check if target is stale.
     *
     * @param sourceDir the project's source directory
     * @param buildDir the project's build output directory
     * @return true if the target needs to be rebuilt
     */
    @Override
    public boolean isStale(final String sourceDir,
        final String buildDir) {

        /*
        System.err.println("isStale() checking " +
            (new File(sourceDir, name)) + " vs " +
            (new File(buildDir, buildFilename)));
        */

        File file = new File(buildDir, buildFilename);
        if (!file.exists()) {
            return true;
        }
        if (!file.isFile()) {
            // Say that the target is stale.  Rebuilding it will generate an
            // error to expose to the user.
            return true;
        }
        File sourceFile = new File(sourceDir, name);
        long time = sourceFile.lastModified();
        return super.isStale(time, buildDir);
    }

    /**
     * Get the build time of the target's build output file.
     *
     * @param buildDir the project's build output directory
     * @return time the modified time of the build output file, or
     * Long.MAX_VALUE if unknown
     */
    public final long getLastBuildTime(final String buildDir) {

        File file = new File(buildDir, buildFilename);
        if (!file.exists()) {
            return Long.MAX_VALUE;
        }
        if (!file.isFile()) {
            return Long.MAX_VALUE;
        }
        return file.lastModified();
    }

    /**
     * Retrieve the number of lines in this file.
     *
     * @param project the project metadata
     * @return the number of lines
     */
    public int getSourceLineCount(final Project project) {
        BufferedReader reader = null;
        int n = 0;

        try {
            reader = new BufferedReader(new
                FileReader(new File(new File(project.getRootDir(),
                            project.getSourceDir()), name)));

            for (String line = reader.readLine(); line != null;
                 line = reader.readLine()) {

                n++;
            }
        } catch (IOException e) {
            // Surface this exception to the user.
            project.displayException(e);
        } finally {
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException e) {
                    // SQUASH
                }
                reader = null;
            }
        }
        return n;
    }

    /**
     * Make human-readable description of this target.
     *
     * @return displayable String
     */
    @Override
    public String toString() {
        return name;
    }

    /**
     * Get all enabled breakpoints in this file.
     *
     * @return the list of breakpoints
     */
    public List<Breakpoint> getBreakpoints() {
        return breakpoints;
    }

    /**
     * Add a breakpoint.
     *
     * @param line the line number
     */
    public void addBreakpoint(final int line) {
        // System.err.println(name + ": add breakpoint at " + line);
        breakpoints.add(new Breakpoint(this, line));
    }

    /**
     * Remove a breakpoint.
     *
     * @param line the line number
     */
    public void removeBreakpoint(final int line) {
        // System.err.println(name + ": remove breakpoint at " + line);
        Breakpoint removeBreakpoint = null;
        for (Breakpoint breakpoint: breakpoints) {
            assert (breakpoint.getTarget() == this);

            if (breakpoint.getLine() == line) {
                removeBreakpoint = breakpoint;
                break;
            }
        }
        if (removeBreakpoint != null) {
            breakpoints.remove(removeBreakpoint);
        }
    }

    /**
     * When this target is run, go to a specific line location.
     *
     * @param line the line number
     */
    public abstract void runToLocation(final int line);

}
